using System;
using System.Collections.Generic;
using System.Text;

namespace Instedd.Labs.Forms.Library
{
    public class Polygon
    {
        public List<PPoint> Points = new List<PPoint>();

        /// <summary>
        /// Tests if a point is inside the polygon
        /// </summary>
        /// <param name="test">The point to test</param>
        /// <returns>True if it is contained in the polygon, false if not</returns>
        public bool IsContained(PPoint test)
        {
            //This function uses a common alorithm for containment called escpe to infinity;
            // Start at the test point and walk a straight line in a particular direction until infinity,
            //if a polygon edge is crossed an odd number of times (1,3,etc) you started INSIDE the polygon,
            //if you cross it zero or an even number of times you were outside.

            //This implementation walks EAST (along the Y=Ty line starting at Tx), and goes for each edge of the polygon
            //testing if the path crosses the edge. If it does, we test whether the intersection is to the east of the
            //test point, if so we 'increment our count' by negating the current test value which starts at zero.

            //this function might fail if the path crosses EXACTLY on a polygon edge. This can be solved in most
            //cases by adding minimum jitter to the test point, since this usually happens when using whole numbers
            //like so:
            //test.Y = test.Y + double.Epsilon;

            int i, j;
            PPoint A, B;
            bool crosses = false;

            for (i = 0; i <= Points.Count - 1; i++)
            {

                //get the two points for the edge
                A = Points[i];
                j = (i == Points.Count - 1) ? 0 : i + 1;
                B = Points[j];

                //see at all if this edge matters (if the test point is inside the edge Y-span)
                if (IsBetween(test.Y, A.Y, B.Y))
                {
                    double m = (A.Y - B.Y) / (A.X - B.X); //get the gradient for the edge

                    if (double.IsInfinity(m))
                    {
                        m = 99999; //an exactly vertical line gets transformed into a ridiculously verticalish line going through A
                    }

                    double c = A.Y - m * A.X; //get the y-intercept of the edge

                    if (m != 0) // if m=0 it is an horizontal edge, therefore we know we can't cross on an eastward path
                    {
                        double intersectX = (test.Y - c) / m;

                        if (intersectX > test.X)
                        {
                            crosses = !crosses;
                        }
                    }
                }
            }
            return crosses;
        }

        private static double Min(double a, double b)
        {
            if (a < b)
                return a;
            else
                return b;
        }

        private static double Max(double a, double b)
        {
            if (a > b)
                return a;
            else
                return b;
        }


        private static bool IsBetween(double test, double a, double b)
        {
            return (test > Min(a, b) && test < Max(a, b));
        }
    }
}
